using System.Linq;
using System.Timers;
using DC2010.Objects.Common;
using DC2010.Objects.Data;
using DC2010.Objects.Logic;

namespace DC2010.Objects
{
    public class PartyLogic : BaseLogic
    {
        public PartyInfo Info { get; set; }
        public PartyGfx Gfx { get; set; }
		
		// Timers
		Timer tMovementTimer;		

        public PartyLogic(PartyData pd)
        {
            Info = new PartyInfo();
            Gfx = Root.I.PartySystem.GetGfx(pd.GfxId);
            Data = pd;
        }
		
		public override void Start()
		{
		    CreatureData cdLeader = GetLeader();

            if (cdLeader.MovementSpeed < 0)
			{
                tMovementTimer = new Timer(cdLeader.MovementSpeed);
				tMovementTimer.Elapsed += delegate
				{
					Move();
				};
				tMovementTimer.Start();	
			}		
		}

        /// <summary>
        /// Calls condition if it is possible to join this party.
        /// </summary>
        /// <param name="cd">Creature which wants to join a party</param>
        /// <returns></returns>
        public bool CanJoinParty(CreatureData cd)
        {
            return Root.I.ConditionSystem.Instances.C_CanJoinParty.CheckCondition(cd, Data);
        }

        public void JoinParty(CreatureData cd)
        {
            if (CanJoinParty(cd))
            {
                Root.I.EffectSystem.Instances.I_JoinParty_Effect.Instant(cd, Data);
            }
        }

		/// <summary>
		/// Add creature to formation. To first free position. 
		/// 
		/// In case creature is single in party it is positioned to the center.
		/// In case creature is second to the party and one creature is already in center. Center creature is moved to position 0
		/// </summary>
        public void AddToFormation(string creatureId)
        {
            PartyData pd = (PartyData)Data;

			// pracovat s ...
            KeyValue kv0 = (from i in pd.IndexedCreatures where i.Key == "0" select i).FirstOrDefault();
            KeyValue kv1 = (from i in pd.IndexedCreatures where i.Key == "1" select i).FirstOrDefault();
            KeyValue kv2 = (from i in pd.IndexedCreatures where i.Key == "2" select i).FirstOrDefault();
            KeyValue kv3 = (from i in pd.IndexedCreatures where i.Key == "3" select i).FirstOrDefault();
			
			KeyValue kv5 = (from i in pd.IndexedCreatures where i.Key == "5" select i).FirstOrDefault();
			
			KeyValue selectedKV = null;
			
			bool added = false;
			
			//only 5 in party?, move 5 to 0.
			if ((kv0==null)&&(kv1==null)&&(kv2==null)&&(kv3==null)&&(kv5!=null))
			{
				kv0 = new KeyValue(kv5.Key, kv5.Value);
				kv5 = null;
			}
			
			// nobody in party? Move monster to the center position...
			if ((kv0==null)&&(kv1==null)&&(kv2==null)&&(kv3==null)&&(kv5==null))
			{
				kv5 = new KeyValue("5", creatureId);
				added = true;
			}

			if ((kv1 == null)&&(!added))
			{
				kv1 = new KeyValue("1", creatureId);
				added = true;
			}
			
			if ((kv1 == null)&&(!added))
			{
				kv1 = new KeyValue("1", creatureId);
				added = true;
			}

			if ((kv2 == null)&&(!added))
			{
				kv2 = new KeyValue("2", creatureId);
				added = true;
			}
			
			if ((kv3 == null)&&(!added))
			{
				kv3 = new KeyValue("3", creatureId);
				added = true;
			}
			
			if ((kv5 == null)&&(!added))
			{
				kv5 = new KeyValue("5", creatureId);
				added = true;
			}
        }

		/// <summary>
		/// Return count of CreaturesData in party
		/// </summary>
        public int Count
        {
            get
            {
                return ((PartyData)Data).Members.Count;
            }
        }

        /// <summary>
        /// This is called when party should turn to right
        /// </summary>
        public void TurnPartyRight()
        {
            PartyData pd = (PartyData) Data;

            // 0 , 1
            // 3 , 2
            // ->
            // 3 , 0
            // 2 , 1
            //
            // So every index is decreased and in case of 0 is turned to 3.

            //find positions
            KeyValue kv0 = (from i in pd.IndexedCreatures where i.Key == "0" select i).FirstOrDefault();
            KeyValue kv1 = (from i in pd.IndexedCreatures where i.Key == "1" select i).FirstOrDefault();
            KeyValue kv2 = (from i in pd.IndexedCreatures where i.Key == "2" select i).FirstOrDefault();
            KeyValue kv3 = (from i in pd.IndexedCreatures where i.Key == "3" select i).FirstOrDefault();

            //ReKey!
            if (kv0!=null) kv0.Key = "3";
            if (kv1 != null) kv1.Key = "0";
            if (kv2 != null) kv2.Key = "1";
            if (kv3 != null) kv3.Key = "2";

            //change party facing.
            pd.Location.TurnRight();
            Root.I.GameSystem.InvertAlt();
        }

        /// <summary>
        /// This is called when party should turn to right
        /// </summary>
        public void TurnPartyLeft()
        {
            PartyData pd = (PartyData)Data;

            // 0 , 1
            // 3 , 2
            // <-
            // 1 , 2
            // 0 , 3
            //
            // So every index is INCREASE and in case of 3 is turned to 0.

            //find positions
            KeyValue kv0 = (from i in pd.IndexedCreatures where i.Key == "0" select i).FirstOrDefault();
            KeyValue kv1 = (from i in pd.IndexedCreatures where i.Key == "1" select i).FirstOrDefault();
            KeyValue kv2 = (from i in pd.IndexedCreatures where i.Key == "2" select i).FirstOrDefault();
            KeyValue kv3 = (from i in pd.IndexedCreatures where i.Key == "3" select i).FirstOrDefault();

            //ReKey!
            if (kv0 != null) kv0.Key = "1";
            if (kv1 != null) kv1.Key = "2";
            if (kv2 != null) kv2.Key = "3";
            if (kv3 != null) kv3.Key = "0";

            //change party facing.
            pd.Location.TurnLeft();
        }


        public void HitWall()
        {
            Root.I.SoundQueue.AddToQueue("Bump");
        }

        private void internal_moveParty(PossibleDirectionsToGo dir)
        {
            PartyData pd = (PartyData)Data;

            //determine new X and Y
            int newX = pd.Location.X;
            int newY = pd.Location.Y;

            switch (dir)
            {
                case PossibleDirectionsToGo.Forward:
                    {
                        switch (pd.Location.Facing)
                        {
                            case Directions.North: { newY--; break; }
                            case Directions.East: { newX++; break; }
                            case Directions.South: { newY++; break; }
                            case Directions.West: { newX--; break; }
                        }
                        break;
                    }
                case PossibleDirectionsToGo.Backward:
                    {
                        switch (pd.Location.Facing)
                        {
                            case Directions.North: { newY++; break; }
                            case Directions.East: { newX--; break; }
                            case Directions.South: { newY--; break; }
                            case Directions.West: { newX++; break; }
                        }
                        break;
                    }
                case PossibleDirectionsToGo.Left:
                    {
                        switch (pd.Location.Facing)
                        {
                            case Directions.North: { newX--; break; }
                            case Directions.East: { newY--; break; }
                            case Directions.South: { newX++; break; }
                            case Directions.West: { newY++; break; }
                        }
                        break;
                    }
                case PossibleDirectionsToGo.Right:
                    {
                        switch (pd.Location.Facing)
                        {
                            case Directions.North: { newX++; break; }
                            case Directions.East: { newY++; break; }
                            case Directions.South: { newX--; break; }
                            case Directions.West: { newY--; break; }
                        }
                        break;
                    }
            }

            int time2move = 1;
 			foreach(CreatureData cd in pd.MembersData)
			{
				int maxLoad = Calculations.MaxLoad(cd);
				int staminaToAdjust = (3*cd.Load)/maxLoad + 1;
				cd.Attributes.Stamina.Incr(-staminaToAdjust);
					
				// Move creature
				cd.Location.X = newX;
                cd.Location.Y = newY;

				// Find slowest character
				if (cd.Attributes.Health.Current != 0)
				{
					int D0W = Calculations.TimeToMove(cd); // Speed of character
					time2move = Calculations.Larger(time2move, D0W);
				};		

				pd.PartyMoveDisableTimer = time2move;


            }

            //adjust currrent location..
            pd.Location.X = newX;
            pd.Location.Y = newY;

            Root.I.GameSystem.InvertAlt();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dir">Direction i want to move marty</param>
        public void MoveParty(PossibleDirectionsToGo dir)
        {
			PartyData pd = (PartyData)Data;

            if (pd.PartyMoveDisableTimer > 0)
            {
                Timer t = new Timer(pd.PartyMoveDisableTimer * 50);
                t.Elapsed += delegate(object sender, ElapsedEventArgs e)
                {
                    internal_moveParty(dir);
                    t.Stop();
                };
                t.Start();
            }
            else
            {
                internal_moveParty(dir);
            }
			


			  
        }

        /// <summary>
        /// Returns party leader in case that party have a leader.
        /// </summary>
        /// <param name="partyLeaderId"></param>
        /// <returns></returns>
        public CreatureData GetLeader(string partyLeaderId)
        {
            var x = (from i in ((PartyData)Data).MembersData
                     where i.Id == partyLeaderId
                     select i).FirstOrDefault();

            return x;
        }

        /// <summary>
        /// Returns party leader in case that party have a leader.
        /// </summary>
        /// <returns>Returns party leader in case that party have a leader.</returns>
        public CreatureData GetLeader()
        {
            string leaderid = ((PartyData) Data).PartyLeaderId;
            return GetLeader(leaderid);
        }

        internal void WakeUp()
        {
            throw new System.NotImplementedException();
        }

        public CreatureData FindFirstMonster(int X, int Y)
        {
            PartyData pd = ((PartyData) Data);
            int membersCount = pd.MembersData.Count;
            int rnd = Root.I.Rnd.Next(membersCount-1);

            return pd.MembersData[rnd];
        }
		
        /// <summary>
        /// This method will return creaturedata from attacked party. It calculates with attacker party position
        /// </summary>
        /// <param name="attackerParty">We need this parameter to determine attacker in party position. Technically right or left side of dungeon.</param>
        /// <param name="attacker">We have to know which direction if facing attacker.</param>
        /// <param name="attackedparty">This time we have to know which direction is facing attacked party. We can attack monsters in back rows or in front rows.</param>
        /// <returns>Creature which will be attacked. Null if there is no creature which can be attacked - Free row</returns>
		public CreatureData FindFirstMonsterICanAttack(PartyData attackerParty, CreatureData attacker, PartyData attackedparty)
		{
            PartyData pd = ((PartyData)Data);

			//is there only one monster in attacked party and it is indexed as 5? Then we have to do nothing.
			
			//attacker facing
		
			// find first monster to attack
			
			// solve it with simple table
			
			// Attacked party facing...
			// North       East        South       West
			// 0 , 1       3 , 0       2 , 3       1 , 2
			// 2 , 3       2 , 1       1 , 0       0 , 3
			////////////////////////////////////////////
			
			// Attacker can access attacked party from four directions
			//
			// Example
			// 1) When attacker approaches from West (he is facing East  attacker.Location.Oposite ) and attacked party is facing North
			//    then we have to determine attacker position in its own party.
			//
			// Attackers party (facing north)
			//   A1, A2
			//   A3, A4
			//
			// If attacker is attacking to the east (he is facing east) than we have to decide which row he will attack.
			// In case attacker is A1 then attacked position in attacked party will be 0 then 1
			// In case attacker is A4 then attacker position in attacked party will be 2 then 3
			//
			// Every creature in party has assigned index - look at following key
			// 0 , 1
            // 3 , 2
			//
			// So we can say:
			// A1 = 0
			// A2 = 1
			// A3 = 3
			// A4 = 2
			//
			// if attacker is in position 0 and facing east and attacked party is facing north then return MonsterInFormationIfAny(0) or MonsterInFormationIfAny(1)
			// takze asi tak....
            return null;
		}
		
		public CreatureData MonsterInFormationIfAny(int formationIndex)
		{
            PartyData pd = ((PartyData)Data);

		    //find positions
            KeyValue kv = (from i in pd.IndexedCreatures where i.Key == formationIndex.ToString() select i).FirstOrDefault();
	
			if (kv == null) return null;			
			return Root.I.CreatureSystem.GetData(kv.Value);
		}
		
		public void SleepStart()
		{
			PartyData pd = ((PartyData) Data);
			
			foreach(CreatureData cd in pd.MembersData)
			{

				cd.L.SleepStart();
			}
		}
		
		public void SleepEnd()
		{
			PartyData pd = ((PartyData) Data);
			
			foreach(CreatureData cd in pd.MembersData)
			{
				cd.L.SleepEnd();
			}
		}
		
		public void Move()
		{
		
		}		
		
    }
}
